<?php

class DataGrabber_GrabRow extends Zend_Db_Table_Row_Abstract
{
    protected $armory = null;
    protected $wowhead = null;
    protected $dungeonsXml = null;
    
    private $dungeonsXmlFile = 'pldkp.xml';
    
    // Some bosses are named after chest not the boss
    protected $bossRename = array (
        195046 => 'Kologarn',
        194957 => 'Mimiron',
        194308 => 'Hodir',
        194312 => 'Thorim',
        194331 => 'Freya',
        181366 => 'Four Horsemen',
    );
    
    // Filtering is setup in init
    protected $filter = array(
        'release' => null,
        'isRaid' => null,
    );
    
    // Items that should be ignored such as emblems
    protected $ignoreItems = array(
        45624,
        40753,
        40752,
        29434,
    );
    
    /**
     * ClassId for all of token items should be 4
     */
    protected $tokenItems = array(
    
    	//Chest:
    	40625 => array('slot' => 5,'level' => 213), //Breastplate of the Lost Conqueror
    	40626 => array('slot' => 5,'level' => 213), //Breastplate of the Lost Protector
    	40627 => array('slot' => 5,'level' => 213), //Breastplate of the Lost Vanquisher
    	45632 => array('slot' => 5,'level' => 226), //Breastplate of the Wayward Conqueror
    	45633 => array('slot' => 5,'level' => 226), //Breastplate of the Wayward Protector
    	45634 => array('slot' => 5,'level' => 226), //Breastplate of the Wayward Vanquisher
    	40610 => array('slot' => 5,'level' => 200), //Chestguard of the Lost Conqueror
    	40611 => array('slot' => 5,'level' => 200), //Chestguard of the Lost Protector
    	40612 => array('slot' => 5,'level' => 200), //Chestguard of the Lost Vanquisher
    	45635 => array('slot' => 5,'level' => 219), //Chestguard of the Wayward Conqueror
    	45636 => array('slot' => 5,'level' => 219), //Chestguard of the Wayward Protector 
    	45637 => array('slot' => 5,'level' => 219), //Chestguard of the Wayward Vanquisher
    	
    	//Head:
    	40631 => array('slot' => 1,'level' => 213), //Crown of the Lost Conqueror
    	40632 => array('slot' => 1,'level' => 213), //Crown of the Lost Protector
    	40633 => array('slot' => 1,'level' => 213), //Crown of the Lost Vanquisher
    	45638 => array('slot' => 1,'level' => 226), //Crown of the Wayward Conqueror
    	45639 => array('slot' => 1,'level' => 226), //Crown of the Wayward Protecto
    	45640 => array('slot' => 1,'level' => 226), //Crown of the Wayward Vanquisher
		40616 => array('slot' => 1,'level' => 200), //Helm of the Lost Conqueror
    	40617 => array('slot' => 1,'level' => 200), //Helm of the Lost Protector
    	40618 => array('slot' => 1,'level' => 200), //Helm of the Lost Vanquisher
    	45647 => array('slot' => 1,'level' => 219), //Helm of the Wayward Conqueror
    	45648 => array('slot' => 1,'level' => 219), //Helm of the Wayward Protector
    	45649 => array('slot' => 1,'level' => 219), //Helm of the Wayward Vanquisher
    	
    	//Gloves:
    	40628 => array('slot' => 10,'level' => 213), //Gauntlets of the Lost Conqueror
    	40629 => array('slot' => 10,'level' => 213), //Gauntlets of the Lost Protector
    	40630 => array('slot' => 10,'level' => 213), //Gauntlets of the Lost Vanquisher
    	45641 => array('slot' => 10,'level' => 226), //Gauntlets of the Wayward Conqueror
    	45642 => array('slot' => 10,'level' => 226), //Gauntlets of the Wayward Protector
    	45643 => array('slot' => 10,'level' => 226), //Gauntlets of the Wayward Vanquisher
    	40613 => array('slot' => 10,'level' => 200), //Gloves of the Lost Conqueror
    	40614 => array('slot' => 10,'level' => 200), //Gloves of the Lost Protector
    	40615 => array('slot' => 10,'level' => 200), //Gloves of the Lost Vanquisher
    	45644 => array('slot' => 10,'level' => 219), //Gloves of the Wayward Conqueror
    	45645 => array('slot' => 10,'level' => 219), //Gloves of the Wayward Protector
    	45646 => array('slot' => 10,'level' => 219), //Gloves of the Wayward Vanquisher
    	
    	//Legs
    	40619 => array('slot' => 7,'level' => 200), //Leggings of the Lost Conqueror
    	40620 => array('slot' => 7,'level' => 200), //Leggings of the Lost Protector
    	40621 => array('slot' => 7,'level' => 200), //Leggings of the Lost Vanquisher
    	45650 => array('slot' => 7,'level' => 219), //Leggings of the Wayward Conqueror
    	45651 => array('slot' => 7,'level' => 219), //Leggings of the Wayward Protector
    	45652 => array('slot' => 7,'level' => 219), //Leggings of the Wayward Vanquisher
    	40634 => array('slot' => 7,'level' => 213), //Legplates of the Lost Conqueror
    	40635 => array('slot' => 7,'level' => 213), //Legplates of the Lost Protector
    	40636 => array('slot' => 7,'level' => 213), //Legplates of the Lost Vanquisher
    	45653 => array('slot' => 7,'level' => 226), //Legplates of the Wayward Conqueror
    	45654 => array('slot' => 7,'level' => 226), //Legplates of the Wayward Protector
    	45655 => array('slot' => 7,'level' => 226), //Legplates of the Wayward Vanquisher
    	
    	//Shoulders:
    	40637 => array('slot' => 3,'level' => 213), //Mantle of the Lost Conqueror
    	40638 => array('slot' => 3,'level' => 213), //Mantle of the Lost Protector
    	40639 => array('slot' => 3,'level' => 213), //Mantle of the Lost Vanquisher
    	45656 => array('slot' => 3,'level' => 226), //Mantle of the Wayward Conqueror
    	45657 => array('slot' => 3,'level' => 226), //Mantle of the Wayward Protector
    	45658 => array('slot' => 3,'level' => 226), //Mantle of the Wayward Vanquisher
    	40622 => array('slot' => 3,'level' => 200), //Spaulders of the Lost Conqueror
    	40623 => array('slot' => 3,'level' => 200), //Spaulders of the Lost Protector
    	40624 => array('slot' => 3,'level' => 200), //Spaulders of the Lost Vanquisher
    	45659 => array('slot' => 3,'level' => 219), //Spaulders of the Wayward Conqueror
    	45660 => array('slot' => 3,'level' => 219), //Spaulders of the Wayward Protector
    	45661 => array('slot' => 3,'level' => 219) //Spaulders of the Wayward Vanquisher
    	);
    	

    public function init()
    {
        $config = Zend_Registry::getInstance()->configuration;
        $grabsFolder = isset($config->datagrabber->folders->grabs) ? $config->datagrabber->folders->grabs : null;
        $wowheadFolder = isset($config->datagrabber->folders->wowhead) ? $config->datagrabber->folders->wowhead : null;
        
        if (!$grabsFolder) {
            throw new Exception('Grabs folder is not defined in config file. Please do so under: datagrabber.folder.grabs');
        } else if (!is_writable($grabsFolder)) {
            throw new Exception('Grabs folder: ' . $grabsFolder . ' is not writeable. ');
        }
        
        if (!$wowheadFolder) {
            throw new Exception('Items folder is not defined in config file. Please do so under: datagrabber.folder.wowhead');
        } else if (!is_writable($wowheadFolder)) {
            throw new Exception('Items folder: ' . $wowheadFolder . ' is not writeable. ');
        }
        
        // Setup a cache in a folder for this fetch
        $folder = $grabsFolder . strtolower(preg_replace('/[^a-zA-Z0-9s]/', '', $this->name));
        $this->dungeonsXmlFile = $folder . '/' . $this->dungeonsXmlFile;
        
        // Create a instance of the armory and wowhead grabber
        $this->armory = new DataGrabber_Armory($folder, 60*60*24*365);
        $this->wowhead = new DataGrabber_Wowhead($wowheadFolder, 60*60*24*365);
        
        // Setup the filter
        $this->filter['isRaid'] = (!$this->isRaid) ? null : (bool) $this->isRaid;
        $this->filter['release'] = (!$this->release) ? null : (int) $this->release;
    }
    
    public function transferToDatabase($test = false)
    {
        // TODO: Do not delete items and bosses from the database, they do a cascade that deletes item -> boss
        
        $this->initializeData();
        $info = $this->getInfo();
        
        if (!$info['initial']['fetched']) {
            throw new Exception('Initial data not fetched.');
        }
        
        if ($info['lootTables']['total'] == 0 || ($info['lootTables']['total'] > $info['lootTables']['fetched'])) {
            throw new Exception('Loot tables not fetched yet.');
        }
        
        if ($info['items']['total'] == 0 || ($info['items']['total'] > $info['items']['fetched'])) {
            throw new Exception('Items not fetched yet.');
        }
        
        if ($test) {
            return;
        }
        
        $dungeons = new Dungeons();
        $bosses = new Bosses();
        $items = new Items();
        
        $bossesItems = new BossesItems();
        
        foreach($this->dungeonsXml->dungeon as $dungeonXml) {
            
            // Delete the dungeon if it exists in the database already
            if ($dungeon = Dungeons::getById((int)$dungeonXml->plId)) {
                $dungeon->delete();
                unset($dungeon);
            }
            
            $dungeonId = $dungeons->insert(array(
                'id' => (int) $dungeonXml->plId,
                'blizzard_id' => (int) $dungeonXml->blizzardId,
                'release' => (int) $dungeonXml->release,
                'name' => (string) $dungeonXml->name,
                'heroic' => (int) $dungeonXml->heroic,
            ));
            
            if (!$dungeonId) {
                throw new Exception('Could not create dungeon: ' . $dungeonXml->name. '('.$dungeonXml->plId.')');
            }
            
            foreach($dungeonXml->bosses->boss as $bossXml) {
                
                // Update existing bosses
                if ($boss = Bosses::getById((int)$bossXml->plId)) {
 
                    $boss->blizzard_id = (int) $bossXml->blizzardId;
                    $boss->dungeon_id = $dungeonId;
                    $boss->name = (string) $bossXml->name;
                    $boss->heroic = (int) $bossXml->heroic;
                    $boss->save();
                    
                    $bossId = $boss->id;
                    
                    unset($boss);
                    
                } else {
                    $bossId = $bosses->insert(array(
                        'id' => (int) $bossXml->plId,
                        'blizzard_id' => (int) $bossXml->blizzardId,
                        'dungeon_id' => $dungeonId,
                        'name' => (string) $bossXml->name,
                        'heroic' => (int) $bossXml->heroic,
                    ));
                }
                
                if (!$bossId) {
                    throw new Exception('Could not create boss: ' . $bossXml->name . '('.$bossXml->plId.')');
                }
                
                foreach($bossXml->items->item as $itemXml) {
                    
                    $itemId = null;
                    
                    // Get the old item if it exists
                    $oldItem = Items::getById((int)$itemXml->id);
                    
                    // If there was a old item and the new data is good we update
                    // Else if there were no old item the new insert the new one
                    // The last case is there is a old item but we have incomplete wowhead data, then we should not update
                    if ($oldItem && !$itemXml->wowheadError) {
                        
                        $oldItem->level = (int) $itemXml->level;
                        $oldItem->class = (int) $itemXml->class;
                        $oldItem->subclass = (int) $itemXml->subclass;
                        $oldItem->name = (string) $itemXml->name;
                        $oldItem->quality = (int) $itemXml->quality;
                        $oldItem->icon = (string) $itemXml->icon;
                        $oldItem->slot = (int) $itemXml->slot;
                        $oldItem->save();
                        
                        $itemId = $oldItem->id;
                        unset($oldItem);
                        
                    } else if (!$oldItem) {
                        
                        // Only insert the availeble data on error
                        if ($itemXml->wowheadError) {
                            
                            $itemId = $items->insert(array(
                                'id' => (int) $itemXml->id,
                                'name' => (string) $itemXml->name,
                            ));
                            
                        } else {
                            
                            $itemId = $items->insert(array(
                                'id' => (int) $itemXml->id,
                                'level' => (int) $itemXml->level,
                                'class' => (int) $itemXml->class,
                                'subclass' => (int) $itemXml->subclass,
                                'name' => (string) $itemXml->name,
                                'quality' => (int) $itemXml->quality,
                                'icon' => (string) $itemXml->icon,
                                'slot' => (int) $itemXml->slot,
                            ));
                            
                        }
                        
                    } else {
                        $itemId = $oldItem->id;
                    }

                    if (!$itemId) {
                        throw new Exception('Could not item: ' . $itemXml->name . '('.$itemXml->id.')');
                    }
                    
                    // Insert the map from boss to item (this will cause a exception if it already exists but thats ok)
                    try {
                        $bossesItemsId = $bossesItems->insert( array(
                            'boss_id' => $bossId,
                            'item_id' => $itemId,
                        ));
                        
                        if (!$bossesItemsId) {
                            throw new Exception('Could not mapping from boss to item: ' . $bossXml->name . '('.$bossXml->plId.') to' . $itemXml->name . '('.$itemXml->id.')');
                        }
                    } catch (Zend_Db_Statement_Exception $e) {}
                }
            }
        }
    }
    
    public function getInfo()
    {
        $result = array(
            'initial' => array (
                'fetched' => false,
                'dungeons' => array(),
                'bosses' => array(),
            ),
            'lootTables' => array (
                'total' => 0,
                'fetched' => 0,
            ),
            'items' => array(
                'total' => 0,
                'fetched' => 0,
                'errors' => 0,
                'missingSlot' => 0,
            ),
        );
        
        if (!$this->dungeonsXml) {
             return $result;
        }
        
        $result['initial']['fetched'] = true;
        
        // Put some info into the array
        foreach($this->dungeonsXml->dungeon as $dungeon) {
            
            $result['initial']['dungeons'][(int)$dungeon->plId] = array(
                'blizzardId' => (int)$dungeon->blizzardId,
                'plId' => (int)$dungeon->plId, 
                'name' => (string)$dungeon->name,
            );
            
            foreach($dungeon->bosses->boss as $boss) {

                $result['initial']['bosses'][(int)$boss->plId] = array(
                    'blizzardId' => (int)$boss->blizzardId,
                    'dungeonPlId' => (int)$dungeon->plId,
                    'plId' => (int)$boss->plId, 
                    'name' => (string)$boss->name,
                );
                
                $result['lootTables']['total']++;
                
                if (isset($boss->items)) {
                    $result['lootTables']['fetched']++;
                    
                    foreach($boss->items->item as $item) {
                        $result['items']['total']++;
                        if (isset($item->fetched)) {
                            $result['items']['fetched']++;
                        }
                        if (isset($item->wowheadError)) {
                            $result['items']['errors']++;
                        }
                        if (isset($item->slot) && $item->slot == 0) {
                            $result['items']['missingSlot']++;
                        }
                    }
                }
            }
        }
        
        return $result;
    }
    
    public function clearImportedData()
    {
        if (is_file($this->dungeonsXmlFile)) {
            unlink($this->dungeonsXmlFile);
        }
    }
    
    public function initializeData() 
    {
        // Don't reload
        if ($this->dungeonsXml) {
            return;
        }
        
        // The file needs to be created
        if (!is_file($this->dungeonsXmlFile)) {
            return;
        }
        
        $this->dungeonsXml = simplexml_load_file($this->dungeonsXmlFile);
    }
    
    public function fetchNextItem()
    {
        if (!$this->dungeonsXml) {
            throw new Exception('Cannot fetch loot tables, data is not initialized.');
        }
        
        foreach($this->dungeonsXml->dungeon as $dungeon) {
            foreach($dungeon->bosses->boss as $i => $boss) {
                if (!isset($boss->items)) {
                    throw new Exception('Loot table not imported for boss.');
                }
                
                foreach($boss->items->item as $item) {
                    
                    // We assume that a item with a level has been imported
                    if (isset($item->fetched)) {
                        continue;
                    }
                    
                    $wowheadData = $this->wowhead->fetchItem($item->id);
                    
                    if (!$wowheadData) {
                        throw new Exception('Error while getting item from wowhead');
                    }
                    
                    $wowheadXml = simplexml_load_string($wowheadData); 
                    
                    if (isset($wowheadXml->error)) {
                        
                        $item->addChild('wowheadError', 1);
                        $item->addChild('fetched', 1);
                        
                        // Save it using DOM, for better formatting
                        $doc = new DOMDocument('1.0');
                        $doc->preserveWhiteSpace = false;
                        $doc->loadXML($this->dungeonsXml->asXML());
                        $doc->formatOutput = true;
                        file_put_contents($this->dungeonsXmlFile, $doc->saveXML());
                        continue;
                    }
                    
                    if (!isset($wowheadXml->item)) {
                        throw new Exception('Item not found in wowhead xml.');
                    }
                    
                    $wowheadItemXml = $wowheadXml->item;
                    
                    $item->addChild('fetched', 1);
                    
                    $item->addChild('subclass ', $wowheadItemXml->subclass ['id']);
                    $item->addChild('quality ', $wowheadItemXml->quality['id']);
                    $item->addChild('icon', $wowheadItemXml->icon);
                    
                    // See if the item is token item (class for token item is 4)
                    if (isset($this->tokenItems[(int)$item->id])) {
                        $item->addChild('slot', $this->tokenItems[(int)$item->id]['slot']);
                        $item->addChild('level', $this->tokenItems[(int)$item->id]['level']);
                        $item->addChild('class ', 4);
                    } else {
                        $item->addChild('slot', $wowheadItemXml->inventorySlot['id']);
                        $item->addChild('level', $wowheadItemXml->level);
                        $item->addChild('class ', $wowheadItemXml->class['id']);
                    }
                    
                    // Save it using DOM, for better formatting
                    $doc = new DOMDocument('1.0');
                    $doc->preserveWhiteSpace = false;
                    $doc->loadXML($this->dungeonsXml->asXML());
                    $doc->formatOutput = true;
                    file_put_contents($this->dungeonsXmlFile, $doc->saveXML());
                    
                    return true;
                }
            }
        }
        
        return false;
    }
    
    public function fetchNextLootTable()
    {
        if (!$this->dungeonsXml) {
            throw new Exception('Cannot fetch loot tables, data is not initialized.');
        }
        
        foreach($this->dungeonsXml->dungeon as $dungeon) {
            foreach($dungeon->bosses->boss as $i => $boss) {
                
                // Skip bosses that already has items
                if (isset($boss->items)) {
                    continue;
                }
                
                $lootTableData = $this->armory->fetchLootable($boss->blizzardId, (($boss->heroic==1)?true:false));
                
                if (!$lootTableData) {
                    throw new Exception('Error while getting loot table from armory');
                }
                
                $lootTableXml = simplexml_load_string($lootTableData);       
 
                if (!isset($lootTableXml->armorySearch->searchResults->items)) {
                    throw new Exception('Loot table contained no items. Someone has problems with xml');
                }
                
                $itemsXml = $boss->addChild('items');
                
                foreach($lootTableXml->armorySearch->searchResults->items->item as $item) {
                    
                    // Ignore some items such as emblems etc.
                    if (in_array($item['id'], $this->ignoreItems)) {
                        continue;
                    }
                    
                    $itemXml = $itemsXml->addChild('item');
                    $itemXml->addChild('id', $item['id']);
                    $itemXml->addChild('name', $item['name']);
                }
                
                // Save it using DOM, for better formatting
                $doc = new DOMDocument('1.0');
                $doc->preserveWhiteSpace = false;
                $doc->loadXML($this->dungeonsXml->asXML());
                $doc->formatOutput = true;
                file_put_contents($this->dungeonsXmlFile, $doc->saveXML());

                return true;
            }
        }
        
        return false;
    }
    
    public function buildInitialXml()
    {
        if (is_file($this->dungeonsXmlFile)) {
            return;   
        }
        
        // Get the dungeon files from the cache or the armory
        $dungeonsData = $this->armory->fetchDungeons();
        $dungeonStringsData = $this->armory->fetchDungeonStrings();
        
        if (!$dungeonsData || !$dungeonStringsData) {
            throw new Exception('Could not retrive the dungeon data from the armory.');
        }
        
        $dungeonsXml = new SimpleXMLElement($dungeonsData);
        $dungeonStringsXml = new SimpleXMLElement($dungeonStringsData);

        // Put all dungeons in this array (in here there is no difference in heroic and non heroic)
        $tempDungeons = array();
        foreach($dungeonsXml->xpath('//dungeon') as $dungeon) {
            
            // Skip ids that are not numeric such as emblemofconquest etc
            $dungeonId = (int) $dungeon['id'];
            if ($dungeonId == 0) continue;
            
            // Create a some data containing the raid
            $data = array(
                'blizzardId' => $dungeonId,
                'blizzardKey' => (string) $dungeon['key'],
                'levelMax' => (int) $dungeon['levelMax'],
                'levelMin' => (int) $dungeon['levelMin'],
                'partySize' => (int) $dungeon['partySize'],
                'isRaid' => (empty($dungeon['raid']) ? 0 : 1),
                'hasHeroic' => (empty($dungeon['hasHeroic']) ? 0 : 1),
            );
            
            $tempDungeons[$dungeonId] = $data;
        }

        // Append with string information
        foreach($dungeonStringsXml->xpath('//dungeon') as $dungeon) {
            
            // Skip ids that are not numeric such as emblemofconquest
            $dungeonId = (int) $dungeon['id'];
            if ($dungeonId == 0) continue;
            
            $tempDungeons[$dungeonId]['name'] = (string) $dungeon['name'];
            $tempDungeons[$dungeonId]['release'] = (int) $dungeon['release'];
            
            $bosses = array();
            foreach($dungeon->boss as $boss) {
                
                $bossId = (int) $boss['id'];
                if ($bossId == 0) continue;

                $bosses[$bossId] = array(
                    'blizzardId' => (int) $boss['id'],
                    'name' => (isset($this->bossRename[$bossId])) ? $this->bossRename[$bossId] : (string) $boss['name'],
                );
            }
            
            $tempDungeons[$dungeonId]['bosses'] = $bosses;
        }
        
        $dungeons = array();
        // Break dungeons into heroic and non heroic and store them in the class dungeon array
        foreach($tempDungeons as $dungeon) {
            
            if ($this->filter['release'] != null && $dungeon['release'] != $this->filter['release']) {
                continue;
            }
            
            if ($this->filter['isRaid'] != null && $dungeon['isRaid'] != $this->filter['isRaid']) {
                continue;
            }
            
            $n = array(
                'plId' => abs(crc32((string)$dungeon['blizzardId'])),
                'name' => $dungeon['name'],
                'levelMax' => $dungeon['levelMax'],
                'levelMin' => $dungeon['levelMin'],
                'partySize' => $dungeon['partySize'],
                'isRaid' => $dungeon['isRaid'],
                'blizzardId' => $dungeon['blizzardId'],
                'blizzardKey' => $dungeon['blizzardKey'],
                'release' => $dungeon['release'],
                'heroic' => 0,
                
            );
            
            foreach ($dungeon['bosses'] as $i => $boss) {
                $n['bosses']['boss'][] = array(
                    'plId' => abs(crc32((string)$boss['blizzardId'])),
                    'name' => $boss['name'],
                    'blizzardId' => $boss['blizzardId'],
                    'heroic' => 0,
                );
            }
            
            $dungeons['dungeons']['dungeon'][] = $n;
            
            // Make another dungeon entry if there is a heroic associated with the dungeon
            if ($dungeon['hasHeroic']) {
                
                $h = array(
                    'plId' => abs(crc32((string)$dungeon['blizzardId'] . '_heroic')),
                    'name' => $dungeon['name'] . ' (Heroic)',
                    'levelMax' => $dungeon['levelMax'],
                    'levelMin' => $dungeon['levelMin'],
                    'partySize' => $dungeon['partySize'],
                    'isRaid' => $dungeon['isRaid'],
                    'blizzardId' => $dungeon['blizzardId'],
                    'blizzardKey' => $dungeon['blizzardKey'],
                    'release' => $dungeon['release'],
                    'heroic' => 1,
                    
                );
                
                foreach ($dungeon['bosses'] as $i => $boss) {
                    $h['bosses']['boss'][] = array(
                        'plId' => abs(crc32((string)$boss['blizzardId'] . '_heroic')),
                        'name' => $boss['name'],
                        'blizzardId' => $boss['blizzardId'],
                        'heroic' => 1,
                    );
                }
                
                $dungeons['dungeons']['dungeon'][] = $h;
            }
        }
        
        $dom = new Pldkp_XML('1.0', 'utf-8');
        $dom->formatOutput = true;
        $dom->preserveWhiteSpace = false;
        $dom->fromMixed($dungeons);
        
        file_put_contents($this->dungeonsXmlFile, $dom->saveXML());
    }
    
}

function show_keys($ar){
        
    echo "<table width='100%' border='1' bordercolor='#6699CC' cellspacing='0' cellpadding='5'>";

    foreach ($ar as $k => $v ) {
            
        echo "<tr valign='top'>";
        echo "<td width='10%'>";            
        echo $k;
        echo "</td>";
            
        echo "<td width='90%'>";
           
        if (is_array($ar[$k])) {
            show_keys ($ar[$k]);
        } else {
            if ($v == null) {
                if (is_numeric($v)) echo 0;
                else echo "<b>NULL</b>";
            }
            else echo $v;
        }
            
        echo "</td></tr>";
     }
     
     echo "</table>";
} 